﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;
using Repo7.BusinessLogic.Local;

namespace Repo7.BusinessLogic.Local
{
    /// <summary>
    /// Utilitarian Methods for File
    /// </summary>
    internal static class FileUtils
    {
        #region Private Members

        /// <summary>
        /// The last Id used for creating RepositoryFile 
        /// </summary>
        private static long lastRepositoryFileId = 0;

        #endregion

        #region Hash File

        /// <summary>
        /// Hash file
        /// </summary>
        /// <param name="filePath">File Path</param>
        /// <returns></returns>
        internal static string GetFileHash(string filePath)
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                return GetFileHash(fs);
            }
        }

        /// <summary>
        /// Hash file
        /// </summary>
        /// <param name="stream">File Stream</param>
        /// <returns></returns>
        internal static string GetFileHash(FileStream stream)
        {
            StringBuilder sb = new StringBuilder();

            if (stream != null)
            {
                stream.Seek(0, SeekOrigin.Begin);

                MD5 md5 = MD5CryptoServiceProvider.Create();
                byte[] hash = md5.ComputeHash(stream);
                foreach (byte b in hash)
                    sb.Append(b.ToString("x2"));

                stream.Seek(0, SeekOrigin.Begin);
            }

            return sb.ToString();
        }

        #endregion

        #region Repository File Creation

        /// <summary>
        /// Creates a Repository File object from a File Info
        /// </summary>
        /// <param name="fileInfo">FileInfo object</param>
        /// <returns>RepositoryFile object</returns>
        internal static RepoFileInfo CreateRepositoryFile(FileInfo fileInfo)
        {
            //Check input
            if (fileInfo == null)
            {
                throw new ArgumentNullException("[CreateRepositoryFile] : Null fileInfo received.");
            }

            RepoFileInfo repositoryFile = new RepoFileInfo()
            {
                Id = ++lastRepositoryFileId,
                Name = fileInfo.Name,
                FullPath = fileInfo.FullName,
                Size = fileInfo.Length,
                Version = 1
            };

            repositoryFile.Hash = GetFileHash(repositoryFile.FullPath);
            return repositoryFile;
        }

        /// <summary>
        /// Get Repository File objects for all files in the specified dir
        /// </summary>
        /// <param name="dirPath">Path of the directory</param>
        /// <returns>List of  RepositoryFile</returns>
        internal static IList<RepoFileInfo> GetRepositoryFilesInDir(string dirPath, string[] fileNamesToIgnore = null)
        {
            //Check input parameter
            if (string.IsNullOrEmpty(dirPath))
            {
                throw new ArgumentOutOfRangeException("[GetRepositoryFilesInDir] : Parameter dirPath is null or empty.");
            }

            //Create directory info
            DirectoryInfo dirInfo = new DirectoryInfo(dirPath);

            //If directory des not exist => throw exception
            if (!dirInfo.Exists)
            {
                throw new ArgumentException(string.Format("[GetRepositoryFilesInDir] : Directory '{0}' does not exist!", dirPath));
            }

            //Create repository files from files in the dir
            FileInfo[] filesInDir = dirInfo.GetFiles();

            //Check for file names to ignore => remove them from repository files
            if (fileNamesToIgnore != null && fileNamesToIgnore.Length > 0)
            {
                IList<FileInfo> filteredFilesInfo = new List<FileInfo>();
                var enumerator = filesInDir.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    if (!fileNamesToIgnore.Contains((enumerator.Current as FileInfo).Name))
                    {
                        filteredFilesInfo.Add(enumerator.Current as FileInfo);
                    }
                }
                filesInDir = filteredFilesInfo.ToArray();
            }

            IList<RepoFileInfo> repositoryFiles = new List<RepoFileInfo>(filesInDir.Length);
            foreach (var fileInfo in filesInDir)
            {
                repositoryFiles.Add(CreateRepositoryFile(fileInfo));
            }

            return repositoryFiles;
        }

        #endregion
    }
}
